home *** CD-ROM | disk | FTP | other *** search
/ TeX 1995 July / TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO / dviware / umddvi / dev / verser2.c < prev   
C/C++ Source or Header  |  1990-10-01  |  19KB  |  759 lines

  1. /*
  2.  * Copyright (c) 1987 University of Maryland Department of Computer Science.
  3.  * All rights reserved.  Permission to copy for any purpose is hereby granted
  4.  * so long as this copyright notice remains intact.
  5.  */
  6.  
  7. #ifndef lint
  8. static char rcsid[] = "$Header: verser2.c,v 2.5 87/06/16 17:15:19 chris Exp $";
  9. #endif
  10.  
  11. /*
  12.  * Verser2 -- Second half of DVI to Versatec driver
  13.  *
  14.  * Reads pre-sorted pages as put out by verser1, and shovels bitmaps
  15.  * out to the Versatec as fast as possible.  Warning:  there is some
  16.  * inline assembly code used in inner loops, where the C compiler
  17.  * produced particuarly poor code.
  18.  *
  19.  * We use a technique known as a `band buffer', where we keep track
  20.  * of what has yet to be written to the Versatec in a buffer that
  21.  * represents a `band' across the current page, analagous to a magnifying
  22.  * bar across the page.  Only the region in the band can be written,
  23.  * and the band moves only downward; this is why verser1 must sort
  24.  * each page, at least by y coordinate.  This also implies that the
  25.  * `tallest' object we can write is the same height as the band.  This
  26.  * is a problem for large characters.  For these there is some (as yet
  27.  * unimplemented) code that will ask for a `part' of each character
  28.  * to be drawn into the band.  The character would then be repeated
  29.  * with a request for the next part when the band has moved down to
  30.  * just below the bottom of the previous part.  Rules are also broken
  31.  * up as appropriate (and that code *is* implemented).
  32.  *
  33.  * Another important point is that the band buffer is treated as a
  34.  * `cylinder' rather than a `strip': we write bits onto the cylinder,
  35.  * then roll it forward over the page, moving the bits off the cylinder
  36.  * and onto the paper, leaving that part of the cylinder clean, ready
  37.  * for more bits.  The variable `CurRow' points at the current row
  38.  * in the buffer/on the cylinder, and `FirstRow' and `LastRow' bound
  39.  * the `dirty' part of the cylinder.  Modular arithmetic suffices to
  40.  * change linear to cylindrical.
  41.  *
  42.  * Whenever CurRow is more than MIN_OUT rows ahead of FirstRow, we
  43.  * write out that much of the cylinder, cleaning it.  This keeps the
  44.  * paper moving, lest the developer soak one spot and produce `streaky'
  45.  * output.
  46.  *
  47.  * Yet another point of note is that because the band always moves
  48.  * `down' on the page, we need only a positive offset from the current
  49.  * row to move to a new row.  This means (among other things) that we
  50.  * can use negative offsets for special purposes.
  51.  */
  52.  
  53. #include <errno.h>
  54. #include <setjmp.h>
  55. #include <stdio.h>
  56. #include <sys/vcmd.h>
  57. #ifdef ACCOUNT_FILE
  58. #include <pwd.h>
  59. #endif ACCOUNT_FILE
  60. #include "types.h"
  61. #include "conv.h"
  62. #include "fio.h"
  63. #include "font.h"
  64. #include "verser.h"
  65.  
  66. #define    SPEED_HACK
  67.  
  68. char    *ProgName;
  69. extern int errno;
  70. extern char *optarg;
  71. extern int optind;
  72.  
  73. /* Globals */
  74. jmp_buf    failbuf;        /* in case of Versatec write() problems */
  75.  
  76. struct font *Fonts[NFONTS];    /* the fonts */
  77.  
  78. char    TeXFontDesc[256];    /* getenv("TEXFONTDESC") from verser1 */
  79.  
  80. int    RasterOrientation;    /* ROT_NORM or ROT_RIGHT, based on HFlag */
  81.  
  82. int    DFlag;            /* -d => output discarded */
  83. int    HFlag;            /* -h => horizontal (rotated bitmaps) */
  84. int    SFlag;            /* -s => silent processing */
  85. int    TFlag;            /* -t => output to tape */
  86. int    Debug;            /* -D => debug flag */
  87.  
  88. char    VBuffer[ROWS][COLUMNS]; /* Versatec band buffer */
  89.  
  90. int    CurRow;            /* current row in buffer */
  91. int    CurCol;            /* current column in buffer */
  92. int    FirstRow;        /* the first row used */
  93. int    LastRow;        /* the last row used */
  94. int    NLines;            /* counts lines; used for pagefeeds */
  95. int    Pages;            /* counts pages; for accounting */
  96.  
  97. int    vp;            /* Versatec file descriptor */
  98.  
  99. int    pltmd[] = {VPLOT, 0, 0};/* print & plot mode, for ioctl */
  100. int    prtmd[] = {VPRINT, 0, 0};
  101.  
  102. #ifdef ACCOUNT_FILE
  103. struct passwd *getpwuid();
  104. #endif ACCOUNT_FILE
  105.  
  106. /*
  107.  * RowsBetween tells how many rows (in cylindrical arithmetic) there
  108.  * are between the first position and the second.  If the second value
  109.  * is less than the first value, add ROWS to do the appropriate modular
  110.  * arithmetic.  We cannot use `%' as C `%' is machine-dependent with
  111.  * respect to negative values.
  112.  */
  113. #define RowsBetween(f, n) ((n) >= (f) ? (n) - (f) : (n) - (f) + ROWS)
  114.  
  115. /*
  116.  * This is it... on your marks ... get set ... main!
  117.  */
  118. main(argc, argv)
  119.     int argc;
  120.     register char **argv;
  121. {
  122.     register int c;
  123.     register char *s;
  124.     int dpi, usermag, num, denom, dvimag;
  125.     int VFlag = 0;
  126. #ifdef ACCOUNT_FILE
  127.     int acct_fd;
  128.  
  129.     acct_fd = open(ACCOUNT_FILE, 1);
  130.     (void) setuid(getuid());
  131. #endif ACCOUNT_FILE
  132.  
  133.     ProgName = *argv;
  134.  
  135.     if (setjmp(failbuf)) {    /* still have to do accounting */
  136. #ifdef ACCOUNT_FILE
  137.         /*
  138.          * Kind of strange to charge for a printout that failed
  139.          * because we ran out of paper, but that was the way they
  140.          * wanted it....
  141.          */
  142.         if (NLines)
  143.             Pages++;/* count the partial page */
  144.         DoAccount(acct_fd);
  145. #endif ACCOUNT_FILE
  146.         exit(1);
  147.         /* NOTREACHED */
  148.     }
  149.     while ((c = getopt(argc, argv, "dstv:D")) != EOF) {
  150.         switch (c) {
  151.  
  152.         case 'd':    /* output to /dev/null */
  153.             DFlag++;
  154.             break;
  155.  
  156.         case 's':    /* silent processing except for errors */
  157.             SFlag++;
  158.             break;
  159.  
  160.         case 't':    /* output to tape (not implemented) */
  161.             TFlag++;
  162.             error(0, 0, "tape option not yet implemented");
  163.             break;
  164.  
  165.         case 'v':    /* Versatec already open as fd <n> */
  166.             VFlag++;
  167.             vp = atoi(optarg);
  168.             break;
  169.  
  170.         case 'D':
  171.             Debug++;
  172.             break;
  173.  
  174.         case '?':
  175.             fprintf(stderr, "Usage: %s [-d] [-s] [-t] [file]\n",
  176.                 ProgName);
  177.             exit(1);
  178.         }
  179.     }
  180.     if (optind < argc)
  181.         if (freopen(argv[optind], "r", stdin) == NULL)
  182.             error(1, 0, "can't open %s", argv[optind]);
  183.  
  184.     HFlag = getchar();
  185.     if ((HFlag >> 1) != VERSION)
  186.         error(1, 0, "input file is not version %d", VERSION);
  187.     HFlag &= 1;
  188.     RasterOrientation = HFlag ? ROT_RIGHT : ROT_NORM;
  189.  
  190.     s = TeXFontDesc;
  191.     c = GetLong(stdin);
  192.     while (--c >= 0)
  193.         *s++ = getchar();
  194.     if (feof(stdin))
  195.         (void) GetByte(stdin);    /* let GetByte do error */
  196.     *s = 0;
  197.  
  198.     dpi = GetLong(stdin);
  199.     usermag = GetLong(stdin);
  200.     num = GetLong(stdin);
  201.     denom = GetLong(stdin);
  202.     dvimag = GetLong(stdin);
  203.     SetConversion(dpi, usermag, num, denom, dvimag);
  204.  
  205.     fontinit(*TeXFontDesc ? TeXFontDesc : (char *) NULL);
  206.     ReadFonts();
  207.  
  208.     if (DFlag) {
  209.         (void) fprintf(stderr, "Output will be discarded\n");
  210.         (void) fflush(stderr);
  211.         vp = open("/dev/null", 1);
  212.     } else {
  213.         if (!VFlag) {
  214.             vp = open(VERSATEC_FILE, 1);
  215.             if (vp < 0) {
  216.                 if (errno == ENXIO)
  217.                     error(1, 0, "\
  218. can't open versatec---already in use");
  219.                 if (errno == EIO)
  220.                     error(1, 0, "\
  221. can't open versatec---device offline");
  222.                 error(1, errno, "can't open %s",
  223.                     VERSATEC_FILE);
  224.             }
  225.         }
  226.         ioctl(vp, VSETSTATE, pltmd);
  227.     }
  228.  
  229.     if (!HFlag)
  230.         CutMarks();    /* initial cut marks */
  231.  
  232.     ReadInput();
  233.  
  234.     FormFeed(0);        /* end up in print mode */
  235.  
  236.     if (!SFlag)
  237.         (void) putc('\n', stderr);
  238.  
  239. #ifdef ACCOUNT_FILE
  240.     DoAccounting(acct_fd);
  241. #endif ACCOUNT_FILE
  242.  
  243.     exit(0);
  244. }
  245.  
  246. #ifdef ACCOUNT_FILE
  247. /*
  248.  * Accounting is done by writing the program name ("tex"), the user name,
  249.  * and the number of pages at the end of the file.  (The program name is
  250.  * for statistics.)
  251.  */
  252. DoAccounting(fd)
  253.     int fd;
  254. {
  255.     register struct passwd *p;
  256.     char buf[128];
  257.  
  258.     if (fd < 0 || (p = getpwuid(getuid())) == 0)
  259.         return;
  260.  
  261.     /*
  262.      * The '+ 2' is because there is an extra page at the end, and
  263.      * because the Versatec does pagefeeds when it is opened.
  264.      */
  265.     (void) sprintf(buf, "tex %s %d\n", p->pw_name, Pages + 2);
  266.     (void) lseek(fd, 0L, 2);
  267.     (void) write(fd, buf, strlen(buf));
  268. }
  269. #endif ACCOUNT_FILE
  270.  
  271. /*
  272.  * Read the font definitions.
  273.  *
  274.  * Anti-streak hack: get the rasters ahead of time, #ifdef SPEED_HACK.
  275.  */
  276. ReadFonts()
  277. {
  278.     register struct font *f, **fp;
  279.     register int c;
  280.     register char *s;
  281. #ifdef SPEED_HACK
  282.     register struct glyph *g;
  283. #endif
  284.     i32 mag, dsize;
  285.     char *fname;
  286.     char nm[512];
  287.  
  288.     if (!SFlag)
  289.         (void) fprintf(stderr, "[fonts:\n");
  290.     fp = Fonts;
  291.     while (GetByte(stdin) == 1) {
  292.         (void) GetLong(stdin);    /* checksum */
  293.         mag = GetLong(stdin);    /* magfactor */
  294.         dsize = GetLong(stdin);    /* design size */
  295.         c = GetLong(stdin);
  296.         s = nm;
  297.         while (--c >= 0)
  298.             *s++ = getchar();
  299.         if (feof(stdin))
  300.             (void) GetByte(stdin);    /* let GetByte do error */
  301.         *s = 0;
  302.         f = GetFont(nm, mag, dsize, "versatec", &fname);
  303.         if (f == NULL) {
  304.             GripeCannotGetFont(nm, mag, dsize, "versatec", fname);
  305.             exit(1);
  306.             /* NOTREACHED */
  307.         }
  308.         if (Debug) {
  309.             (void) fprintf(stderr, "[%s -> %s]\n",
  310.                 Font_TeXName(f), fname);
  311.             (void) fflush(stderr);
  312.         }
  313.         if (!SFlag) {
  314.             register char *t = fname;
  315.  
  316.             s = fname;
  317.             while (*s)
  318.                 if (*s++ == '/' && *s)
  319.                     t = s;
  320.             (void) fprintf(stderr, " %s\n", t);
  321.         }
  322. #ifdef SPEED_HACK
  323.         for (c = 0; c < 128; c++) {
  324.             g = GLYPH(f, c);
  325.             if (GVALID(g))
  326.                 (void) RASTER(g, f, RasterOrientation);
  327.         }
  328. #endif
  329.         *fp++ = f;
  330.     }
  331.     if (!SFlag)
  332.         (void) fprintf(stderr, "]\n");
  333. }
  334.  
  335. /*
  336.  * Read the input stream, decode it, and put character rasters or rules at
  337.  * the positions given.
  338.  */
  339. ReadInput()
  340. {
  341.     register int yx, fcp, height;
  342.  
  343.     /*
  344.      * Loop forever.  I had a `for (;;)' but everything crept off the
  345.      * right side of the screen. 
  346.      */
  347. next:
  348.     fGetLong(stdin, yx);    /* position */
  349.     fGetLong(stdin, fcp);    /* character, most likely */
  350.     if (feof(stdin))
  351.         return;        /* done */
  352.  
  353.     /*
  354.      * A `position' of -1 indicates either a rule or an end of page.
  355.      * Anything else is a character.
  356.      */
  357.     if (yx != -1) {        /* place character */
  358.         register struct glyph *g;
  359.         register struct font *f;
  360.         register int fnum;
  361.  
  362.         /*
  363.          * Any delta-y required is stored in the upper 16 bits of yx.
  364.          */
  365.         if ((height = yx >> 16) != 0)
  366.             MoveDown(height);
  367.         /*
  368.          * Extract the x, font, char, and part info into CurCol,
  369.          * fnum, yx, and fcp.
  370.          */
  371.         CurCol = yx & 0xffff;
  372.         fnum = fcp >> FONTSHIFT;
  373.         yx = (fcp >> CHARSHIFT) & CHARMASK;
  374.         fcp = fcp & PARTMASK;
  375.         f = Fonts[fnum];    /* trusting */
  376.         g = GLYPH(f, yx);
  377.  
  378.         /*
  379.          * In case this character does not fit, write
  380.          * out the used part of the band.  It had better
  381.          * fit afterward....
  382.          */
  383.         height = g->g_height;
  384.         if (height >= ROWS - RowsBetween(FirstRow, CurRow))
  385.             DumpTopOfBand();
  386.         if (fcp)    /* cannot handle these yet */
  387.             error(0, 0, "\
  388. part code not implemented; skipping char %d in %s",
  389.                 yx, f->f_path);
  390.         else if (HASRASTER(g)) {
  391. #ifdef SPEED_HACK
  392.             /* XXX, but saves time */
  393.             VWriteChar(g->g_raster, height, g->g_width);
  394. #else
  395.             VWriteChar(RASTER(g, f, RasterOrientation),
  396.                 height, g->g_width);
  397. #endif
  398.         }
  399.         /* dump if we can do at least MIN_OUT rows */
  400.         if (RowsBetween(FirstRow, CurRow) > MIN_OUT)
  401.             DumpTopOfBand();
  402.         goto next;    /* done with character */
  403.     }
  404.  
  405.     /*
  406.      * If the `character' is negative, we need to move down first,
  407.      * possibly because this is an end-of-page.  If this is not the
  408.      * end of the page, it must be a rule.
  409.      */
  410.     if (fcp < 0) {        /* move down */
  411.         yx = -fcp;
  412.         fGetLong(stdin, fcp);    /* junk */
  413.         fGetLong(stdin, fcp);
  414.         if (fcp == 0) {    /* end page */
  415.             /* dump entire band */
  416.             WriteBuf(&VBuffer[0][0], FirstRow, LastRow, 1);
  417.             CurRow = LastRow = FirstRow;
  418.             if (!HFlag) {
  419.                 WriteBlanks(yx - NLines);
  420.                 CutMarks();
  421.             } else
  422.                 FormFeed(1);
  423.             if (!SFlag)
  424.                 (void) fprintf(stderr, ".");
  425.             NLines = 0;
  426.             Pages++;
  427.             goto next;    /* all done */
  428.         }
  429.  
  430.         MoveDown(yx);    /* must be a rule; move down by yx rows */
  431.     }
  432.  
  433.     /*
  434.      * At this point we have a rule to put at the current
  435.      * position, CurRow.
  436.      */
  437.     height = (fcp & 0xff00) >> 8;
  438.     /* make sure it fits */
  439.     if (height >= ROWS - RowsBetween(FirstRow, CurRow))
  440.         DumpTopOfBand();
  441.     VWriteRule(fcp);
  442.     goto next;        /* done with rule */
  443. }
  444.  
  445. /*
  446.  * Write the given raster for the given character.
  447.  *
  448.  * Basically, the task is to move bits from the raster to the Versatec
  449.  * buffer.  However, because the character being plotted can be on an
  450.  * arbitrary bit boundary, things are not as simple as we might like.
  451.  * The solution used here is to shift each raster value right, OR it
  452.  * into the buffer, then (at the next location) OR in the bits that
  453.  * `fell off the right edge'.
  454.  */
  455. VWriteChar(rastp, height, width)
  456.     char *rastp;        /* raster pointer */
  457.     int height, width;    /* height & width of char */
  458. {
  459.     register char *bp;    /* Versatec buffer pointer [r11] */
  460.     register char *rp;    /* raster pointer       [r10] */
  461.     register int rshift;    /* right shift index       [r9]  */
  462.     register int lshift;    /* left shift index       [r8]  */
  463.     register int j;        /* width loop downcounter */
  464.     register int o;        /* offset to next row in buffer */
  465.     int row;        /* current row in buffer */
  466.     int col;        /* column in buffer of left edge */
  467.     int i;            /* height loop downcounter */
  468.     int w;            /* raster width (bytes) */
  469.  
  470.     if ((rp = rastp) == NULL)
  471.         return;        /* an all-white character (`cannot happen') */
  472.  
  473.     row = CurRow;
  474.     col = CurCol >> 3;
  475.     i = height;
  476.     w = (width + 7) >> 3;
  477.     o = COLUMNS - w;
  478.  
  479. #if defined(lint) || !defined(vax)
  480.     rshift = CurCol & 7;
  481.     lshift = 8 - rshift;
  482. #else lint || !vax
  483.     rshift = -(CurCol & 7);    /* Vax does '>>' as negative '<<' */
  484.     lshift = 8 + rshift;
  485. #endif lint || !vax
  486.     bp = &VBuffer[row][col];
  487.  
  488. #define avoiding_shifts_is_faster    /* but is it??? */
  489. #ifdef avoiding_shifts_is_faster
  490.     /*
  491.      * One out of eight or so times, the shift values will be
  492.      * zero.  This makes the code run faster.
  493.      */
  494.     if (rshift == 0) {
  495.         while (--i >= 0) {
  496.             j = w;
  497.             while (--j >= 0)
  498.                 *bp++ |= *rp++;
  499.             if (++row >= ROWS) {
  500.                 row = 0;
  501.                 bp = &VBuffer[0][col];
  502.             } else
  503.                 bp += o;
  504.         }
  505.     } else
  506. #endif
  507.     {
  508.         while (--i >= 0) {
  509.             j = w;
  510.             while (--j >= 0) {
  511. #if defined(lint) || !defined(vax)
  512.                 *bp++ |= (*rp & 255) >> rshift;
  513.                 *bp |= (*rp++ & 255) << lshift;
  514. #else lint || !vax
  515.                 /*
  516.                  * THE FOLLOWING ASSEMBLY CODE IS INSERTED
  517.                  * BECAUSE THE COMPILER CAN'T OPTIMIZE THE
  518.                  * C CODE WORTH A DARN
  519.                  */
  520.                 asm("    movzbl    (r10)+,r1 # *rp++ & 255");
  521.                 asm("    ashl    r9,r1,r0  # >> rshift");
  522.                 asm("    bisb2    r0,(r11)+ # *bp++ |=");
  523.                 asm("    ashl    r8,r1,r0  # << lshift");
  524.                 asm("    bisb2    r0,(r11)  # *bp |=");
  525. #endif lint || !vax
  526.             }
  527.             if (++row >= ROWS) {
  528.                 row = 0;
  529.                 bp = &VBuffer[0][col];
  530.             } else
  531.                 bp += o;
  532.         }
  533.     }
  534.  
  535.     j = height + CurRow - 1;/* have now set bits this far */
  536.     if (j >= ROWS)
  537.         j -= ROWS;    /* keep it modular */
  538.  
  539.     /*
  540.      * There are two cases.  Either the buffer is not currently wrapped,
  541.      * in which case the regions past LastRow or before FirstRow extend
  542.      * it; or it is wrapped, in which case the region between LastRow
  543.      * and FirstRow extends it:
  544.      *
  545.      *        case 1         case 2
  546.      *       --------        --------
  547.      *       |      |    last  ->| XXXX |
  548.      * first ->| XXXX |        |      |
  549.      *       | XXXX |        |      |
  550.      * last  ->| XXXX |    first ->| XXXX |
  551.      *       |      |        | XXXX |
  552.      *       --------        --------
  553.      *
  554.      * The `X's mark the region that is in use; the blank spaces
  555.      * mark the region that causes the `last' value to change.
  556.      */
  557.     if (FirstRow <= LastRow) {
  558.         /* first case: not wrapped */
  559.         if (j < FirstRow || j > LastRow)
  560.             LastRow = j;
  561.     } else {
  562.         /* second case: wrapped */
  563.         if (j > LastRow && j < FirstRow)
  564.             LastRow = j;
  565.     }
  566. }
  567.  
  568. /*
  569.  * Write a rule at the current row according to the (packed) information in
  570.  * 'info'.  This includes the x position and the height and width of the
  571.  * rule.
  572.  */
  573. VWriteRule(info)
  574.     int info;
  575. {
  576.     register char *bp;    /* buffer pointer */
  577.     register int j;
  578.     register int lbits;    /* bits along left */
  579.     register int rbits;    /* bits along right */
  580.     register int o;        /* offset to next row */
  581.     register int i;
  582.     register int full;    /* number of 8 bit words to set */
  583.     register int height;    /* rule height */
  584.     register int width;    /* rule width */
  585.     register int row;
  586.     register int col;
  587.  
  588.     i = info;
  589.     CurCol = (i & 0x7fff0000) >> 16;
  590.     height = (i & 0xff00) >> 8;
  591.     width = i & 0xff;
  592.     col = CurCol >> 3;
  593.     row = CurRow;
  594.     j = CurCol & 7;        /* bit # of start position */
  595.     lbits = 0xff >> j;    /* bits to set along left edge */
  596.     /* there are 8-j bits set in lbits */
  597.     o = 8 - j - width;
  598.     if (o > 0) {        /* then lbits has o too many bits set */
  599.         lbits >>= o;
  600.         lbits <<= o;    /* puts zeros into o righthand bits */
  601.         rbits = 0;
  602.         full = 0;
  603.     } else {
  604.         i = (CurCol + width) & 7;    /* bit # of ending position */
  605.         rbits = 0xff00 >> i;    /* bits to set along right edge */
  606.         /* there are i bits set in rbits (well, in the low byte) */
  607.         full = (width - i - (8 - j)) >> 3;
  608.     }
  609.     bp = &VBuffer[row][col];
  610.     i = height;
  611.  
  612.     /* Often "full" is zero, which makes things faster */
  613.     if (full) {        /* oh well */
  614.         o = COLUMNS - full - 1;
  615.         while (--i >= 0) {
  616.             *bp++ |= lbits;
  617.             for (j = full; --j >= 0;)
  618.                 *bp++ |= 0xff;
  619.             *bp |= rbits;
  620.             if (++row >= ROWS) {
  621.                 row = 0;
  622.                 bp = &VBuffer[0][col];
  623.             } else
  624.                 bp += o;
  625.         }
  626.     } else {
  627.         o = COLUMNS - 1;
  628.         while (--i >= 0) {
  629.             *bp++ |= lbits;
  630.             *bp |= rbits;
  631.             if (++row >= ROWS) {
  632.                 row = 0;
  633.                 bp = &VBuffer[0][col];
  634.             } else
  635.                 bp += o;
  636.         }
  637.     }
  638.     i = CurRow + height - 1;
  639.     if (i >= ROWS)
  640.         i -= ROWS;
  641.     /*
  642.      * This is another way of expressing both cases 1 and 2 in
  643.      * VWriteChar().  I think the other way is likely to be
  644.      * faster, and characters occur far more frequently; but this
  645.      * is the more readable by far.
  646.      */
  647.     if (RowsBetween(FirstRow, LastRow) < RowsBetween(FirstRow, i))
  648.         LastRow = i;
  649. }
  650.  
  651. /*
  652.  * Dump out the top portion of the band (rows [Firstrow, CurRow)).
  653.  */
  654. DumpTopOfBand()
  655. {
  656.  
  657.     /*
  658.      * To exclude CurRow, subtract one, but modularly, modularly!
  659.      */
  660.     WriteBuf(&VBuffer[0][0], FirstRow, CurRow ? CurRow - 1 : ROWS - 1, 1);
  661.     FirstRow = CurRow;
  662. }
  663.  
  664. /*
  665.  * Move the current row in the band buffer down by delta rows, by,
  666.  * if necessary, writing out the currently-used portion of the buffer.
  667.  */
  668. MoveDown(delta)
  669.     register int delta;
  670. {
  671.  
  672.     if (delta >= ROWS - RowsBetween(FirstRow, CurRow)) {
  673.         /*
  674.          * Need to roll the cylinder forward.  Write out the used
  675.          * part, and then write as many blank lines as necessary.
  676.          */
  677.         WriteBuf(&VBuffer[0][0], FirstRow, LastRow, 1);
  678.         WriteBlanks(delta - RowsBetween(CurRow, LastRow) - 1);
  679.         CurRow = LastRow = FirstRow;    /* band is now empty */
  680.     } else {
  681.         /*
  682.          * Because RowsBetween returns nonnegative integers, we
  683.          * know delta <= ROWS, so can do mod more quickly thus:
  684.          */
  685.         CurRow += delta;    /* result < 2*ROWS */
  686.         if (CurRow >= ROWS)
  687.             CurRow -= ROWS;    /* now result < ROWS */
  688.     }
  689. }
  690.  
  691. /*
  692.  * Write the lines between the first and last inclusive from the given
  693.  * buffer.  If 'cl', clear after writing.
  694.  */
  695. WriteBuf(buf, first, last, cl)
  696.     register char *buf;
  697.     register int first, last;
  698. {
  699.  
  700.     if (first > last) {    /* recursively do wrapped part first */
  701.         WriteBuf(buf, first, ROWS - 1, cl);
  702.         first = 0;
  703.     }
  704.     buf = &buf[first * COLUMNS];
  705.     last = COLUMNS * (first = last - first + 1);
  706.  
  707.     /*
  708.      * If the write fails, the Versatec is probably out of paper, and in
  709.      * any case, things are probably in bad shape. 
  710.      */
  711.     if (write(vp, buf, last) != last) {
  712.         error(0, errno, "Versatec write error");
  713.         longjmp(failbuf, 1);
  714.     }
  715.     if (cl)
  716.         bzero(buf, (unsigned) last);
  717.     NLines += first;
  718. }
  719.  
  720. /*
  721.  * Write 'n' blank lines.
  722.  */
  723. WriteBlanks(n)
  724.     register int n;
  725. {
  726.     register int k;
  727.     static char nullbuf[MIN_OUT][COLUMNS];
  728.  
  729.     while (n > 0) {
  730.         k = n > MIN_OUT ? MIN_OUT : n;
  731.         WriteBuf(&nullbuf[0][0], 0, k - 1, 0);
  732.         n -= k;
  733.     }
  734. }
  735.  
  736. /*
  737.  * Write cut marks.  We borrow row 0 of VBuffer for this.
  738.  */
  739. CutMarks()
  740. {
  741.     register short *bp = (short *) &VBuffer[0][0];
  742.  
  743.     *bp = 0xffff;
  744.     bp[(COLUMNS / 2) - 1] = 0xffff;
  745.     WriteBuf(&VBuffer[0][0], 0, 0, 1);
  746. }
  747.  
  748. /*
  749.  * Perform a page feed.  Restore plot mode if `setplot'.
  750.  */
  751. FormFeed(setplot)
  752.     int setplot;
  753. {
  754.     ioctl(vp, VSETSTATE, prtmd);
  755.     (void) write(vp, "\f", 2); /* \0 really IS required occasionally */
  756.     if (setplot)
  757.         ioctl(vp, VSETSTATE, pltmd);
  758. }
  759.